package filters;

import histogram.HistogramDescr;
import java.util.LinkedList;

public class CleanRegionNeiro {

    int markedRegions[][];
    int originalMatrix[][];
    int cleanResult[][];
    final int START_COMP_LABEL = 100;
    int curCompLabel = START_COMP_LABEL + 1;
    int height;
    int width;
    int tmpMark[][];
    final int minRegionSize = 1000;

    public CleanRegionNeiro(int[][] originalMatrix) {
        this.originalMatrix = originalMatrix;
        height = originalMatrix[0].length;
        width = originalMatrix.length;

        markedRegions = new int[width][height];
        cleanResult = new int[width][height];

        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                cleanResult[i][j] = originalMatrix[i][j];
            }
        }

    }

    public void doClean() {
        System.out.println("start clean");
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (markedRegions[i][j] < START_COMP_LABEL) {
                    findComponent(i, j, originalMatrix[i][j]);
                    curCompLabel++;
                }
            }
        }
        System.out.println("cleaned");
    }

    private void findComponent(int xPoint, int yPoint, int type) {
        LinkedList<Integer> xQueue = new LinkedList<Integer>();
        LinkedList<Integer> yQueue = new LinkedList<Integer>();

        xQueue.push(xPoint);
        yQueue.push(yPoint);

        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};

        int regionSize = 0;

        int neibMitath = 0;
        int neibLipid = 0;
        int neibBack = 0;

        while (!xQueue.isEmpty()) {
            int x = xQueue.pop();
            int y = yQueue.pop();

            if (x >= width || y >= height || x < 0 || y < 0) {
                continue;
            }

            if (originalMatrix[x][y] == type && markedRegions[x][y] < START_COMP_LABEL) {
                markedRegions[x][y] = curCompLabel;

                regionSize++;

                for (int i = 0; i < 4; i++) {

                    int tmpx = x + dx[i];
                    int tmpy = y + dy[i];
                    if (tmpx >= width || tmpy >= height || tmpx < 0 || tmpy < 0) {
                        continue;
                    }

                    switch (originalMatrix[tmpx][tmpy]) {
                        case HistogramDescr.MITAH:
                            if (originalMatrix[tmpx][tmpy] != type) {
                                neibMitath++;
                            }
                            break;
                        case HistogramDescr.LIPID:
                            if (originalMatrix[tmpx][tmpy] != type) {
                                neibLipid++;
                            }
                            break;
                        case HistogramDescr.BACK:
                            if (originalMatrix[tmpx][tmpy] != type) {
                                neibBack++;
                            }
                            break;
                    }

                    xQueue.push(x + dx[i]);
                    yQueue.push(y + dy[i]);
                }
            }
        }

        if (regionSize < minRegionSize) {
            if (neibBack >= neibLipid && neibBack >= neibMitath) {
                paintComponent(xPoint, yPoint, curCompLabel, HistogramDescr.BACK);
            } else if (neibMitath >= neibLipid && neibMitath >= neibLipid) {
                paintComponent(xPoint, yPoint, curCompLabel, HistogramDescr.MITAH);
            } else {
                paintComponent(xPoint, yPoint, curCompLabel, HistogramDescr.LIPID);
            }
        }

        if (type == HistogramDescr.LIPID && neibMitath > 2 * (neibLipid + neibBack)) {
            paintComponent(xPoint, yPoint, curCompLabel, HistogramDescr.MITAH);
        }
        if (type == HistogramDescr.MITAH && neibLipid > 2 * (neibMitath + neibBack)) {
            paintComponent(xPoint, yPoint, curCompLabel, HistogramDescr.LIPID);
        }

//        System.out.println(regionSize + "  " + neibBack + " " + neibLipid + " " + neibMitath);
    }

    void paintComponent(int xPoint, int yPoint, int mark, int newType) {
        LinkedList<Integer> xQueue = new LinkedList<Integer>();
        LinkedList<Integer> yQueue = new LinkedList<Integer>();

        xQueue.push(xPoint);
        yQueue.push(yPoint);

        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};

        while (!xQueue.isEmpty()) {
            int x = xQueue.pop();
            int y = yQueue.pop();

            if (x >= width || y >= height || x < 0 || y < 0) {
                continue;
            }

            if (markedRegions[x][y] == mark && cleanResult[x][y] != newType) {
                cleanResult[x][y] = newType;

                for (int i = 0; i < 4; i++) {
                    xQueue.push(x + dx[i]);
                    yQueue.push(y + dy[i]);
                }
            }
        }
    }

    public int[][] getCleanResult() {
        return cleanResult;
    }
}
